﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.IO;
namespace ZeusMapCollisionEditor
{
    public partial class Form1 : Form
    {
        private string _mapFile;                //当前显示的地图
        private int _w;                         //网格宽度
        private int _h;                         //网格高度
        private Color _collisionColor;          //碰撞网格颜色
        private Rectangle _mapShowRect;         //保存地图所显示在的窗口的区域
        private PointF _mapImagePos;            //保存地图相对于图片显示的坐标
        private Image _mapImage;                //当前地图
        private Image _mapImageBuffer;          //地图显示图片内存中的缓冲
        private bool[,] _mapCollision;          //描述地图碰撞
        private RectangleF[,] _mapCellRect;     //保存单元格所在矩形
        private bool _mouseDown;                //鼠标是否按下
        private TextureBrush _mapTexBrush;      //地图的纹理笔刷
        private PointF _moveLastPos;            //移动状态 鼠标最后所在的坐标 对于地图缓冲区计算而言
        private float _percent;                 //当前显示的百分比

        private int _yLast;
        private int _xLast;

        private Graphics _g;

        public Form1()
        {
            InitializeComponent();
            _collisionColor = Color.FromArgb(0x88,64, 0, 64);
            myButton1.ShowColor = _collisionColor;
            _w =int.Parse( textBox_w.Text );
            _h = int.Parse(textBox_h.Text);
            _percent = 1.0f;
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true); 
        }

        private void ShowImageBuffer()  //将缓冲区内容填到窗口上
        {
            if (_mapImageBuffer != null)
            {

                Graphics g = Graphics.FromHwnd(this.Handle);
                g.Clip = new Region(_mapShowRect);

                //用来显示可见区域的缓冲区
                Bitmap bitmapBuffer = new Bitmap(_mapShowRect.Width, _mapShowRect.Height);
                Graphics gBuffer = Graphics.FromImage(bitmapBuffer);

                //地图的绘制
                RectangleF showRect = new RectangleF();
                showRect.Location = _mapImagePos;
                showRect.Width = _mapShowRect.Width / _percent;
                showRect.Height = _mapShowRect.Height / _percent;

                gBuffer.Clear(Color.FromKnownColor(KnownColor.Window));
                gBuffer.DrawImage(_mapImageBuffer, new RectangleF(0, 0, _mapShowRect.Width, _mapShowRect.Height), showRect, GraphicsUnit.Pixel);

                TextureBrush tb = new TextureBrush(bitmapBuffer);
                tb.TranslateTransform(_mapShowRect.X, _mapShowRect.Y);
                g.FillRegion(tb, g.Clip);

                tb.Dispose();
                bitmapBuffer.Dispose();
                gBuffer.Dispose();
                g.Clip.Dispose();
                g.Dispose();
            }

        }


        private void RefreshImageBuffer()
        {

            //创建一个bitmap缓冲区
            if (_mapImageBuffer != null) { _mapImageBuffer.Dispose(); }
            _mapImageBuffer = new Bitmap(_mapImage.Width, _mapImage.Height);
            //使用png绘制到bitmap缓冲区
            using (Graphics g = Graphics.FromImage(_mapImageBuffer))
            {
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                g.DrawImage(_mapImage, 0, 0, _mapImage.Width, _mapImage.Height);
            }

            //计算格子数目
            if (_w == 0 || _h == 0) { return; }
            int row = 0, clomn = 0;
            if (_mapImage.Height % _h == 0)
            {
                row = _mapImage.Height / _h;
            }
            else
            {
                row = _mapImage.Height / _h + 1;
            }
            if (_mapImage.Width % _w == 0)
            {
                clomn = _mapImage.Width / _w;
            }
            else
            {
                clomn = _mapImage.Width / _w + 1;
            }
            _mapCellRect = new RectangleF[row, clomn];
            _mapCollision = new bool[row, clomn];
            //计算单元格所在矩形 相对于地图
            for (int i = 0; i < _mapCellRect.GetLength(0); ++i)
            {
                for (int j = 0; j < _mapCellRect.GetLength(1); ++j)
                {
                    _mapCellRect[i, j] = new Rectangle(j * _w + 1, i * _h + 1, _w - 1, _h - 1);
                }
            }
            Graphics gBuffer = Graphics.FromImage(_mapImageBuffer);
            //格子绘制到缓冲区内
            Pen p = new Pen(Color.FromArgb(0x60,0xFF,0xFF,0xFF),1);
            for (int i = 0; i <= _mapCollision.GetLength(0); ++i)
            {
                //画行
                gBuffer.DrawLine(p, 0, i * _h, _mapImage.Width, i * _h);
            }
            for (int i = 0; i <= _mapCollision.GetLength(1); ++i)
            {
                //画列
                gBuffer.DrawLine(p, i * _w, 0, i * _w, _mapImage.Height);
            }
        }

        private void button_add_Click(object sender, EventArgs e)
        {
            //向列表 添加图片png图片
            openFileDialog1.Filter = "Map图片文件(*.png)|*.png";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                for (int i = 0; i < openFileDialog1.FileNames.GetLength(0); ++i)
                {
                    ListViewItem lvi = listView_file.Items.Add(new ListViewItem(openFileDialog1.SafeFileNames[i]));
                    lvi.ToolTipText = openFileDialog1.FileNames[i];
                }
                
            }
        }

        private void button_delSel_Click(object sender, EventArgs e)
        {
            //删除选中
            foreach (ListViewItem lvi in listView_file.SelectedItems)
            {
                listView_file.Items.Remove(lvi);
            }
        }

        private void button_clear_Click(object sender, EventArgs e)
        {
            //清空列表
            listView_file.Items.Clear();
        }

        private void listView_file_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (listView_file.SelectedIndices.Count == 1)
            {
                this.Refresh();
                _mapFile = listView_file.SelectedItems[0].ToolTipText;

                //地图加载
                if (_mapImage != null) { _mapImage.Dispose(); }
                _mapImage = Image.FromFile(_mapFile);

                if (_mapTexBrush != null) { _mapTexBrush.Dispose(); }
                _mapTexBrush = new TextureBrush(_mapImage, WrapMode.Clamp);

                //计算显示区域
                _mapImagePos.X = 0;
                _mapImagePos.Y = 0;
                
                Rectangle rt = new Rectangle(12, 164, 938, 596);
                if (_mapImage.Width < 926)
                {
                    rt.Width = _mapImage.Width;
                }
                if (_mapImage.Height < 432)
                {
                    rt.Height = _mapImage.Height;
                }
                _mapShowRect = rt;

                //单元格计算 绘制缓冲区
                RefreshImageBuffer();
                _percent = 1.0f;
                textBox_percent.Text = "100";
                ShowImageBuffer();
            }
        }

        private void button_refresh_Click(object sender, EventArgs e)
        {
            ShowImageBuffer();
        }

        private void myButton1_Click(object sender, EventArgs e)
        {
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                colorDialog1.Color = Color.FromArgb(0x88, colorDialog1.Color.R,
                    colorDialog1.Color.G, colorDialog1.Color.B);
                _collisionColor = colorDialog1.Color;
                myButton1.ShowColor = colorDialog1.Color;
            }
        }

        private void textBox_w_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar != '\b')//这是允许输入退格键
            {
                if ((e.KeyChar < '0') || (e.KeyChar > '9'))//这是允许输入0-9数字
                {
                    e.Handled = true;
                }
            }
        }

        private void textBox_h_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar != '\b')//这是允许输入退格键
            {
                if ((e.KeyChar < '0') || (e.KeyChar > '9'))//这是允许输入0-9数字
                {
                    e.Handled = true;
                }
            }
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            ShowImageBuffer();
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            _mouseDown = false;
            if (e.Button == MouseButtons.Left
                && radioButton_brush.Checked
                && _g != null)
            {
                _g.Dispose();
            }
        }

        private void textBox_w_TextChanged(object sender, EventArgs e)
        {
            _w = int.Parse(textBox_w.Text);
            if (checkBox_prop.Checked)
            {
                _h = _w;
                textBox_h.Text = _h.ToString();
            }
            RefreshImageBuffer();
            ShowImageBuffer();
        }

        private void textBox_h_TextChanged(object sender, EventArgs e)
        {
            _h = int.Parse(textBox_h.Text);
            if (checkBox_prop.Checked)
            {
                _w = _h;
                textBox_w.Text = _w.ToString();
            }
            RefreshImageBuffer();
            ShowImageBuffer();
        }

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            //坐标根据比例变换
            PointF p = new PointF((e.X - _mapShowRect.X) / _percent, (e.Y - _mapShowRect.Y) / _percent);

            //换算位置
            int _y = (int)(_mapImagePos.Y + p.Y) / _h;
            //控制位置不出界
            if (_y >= _mapCellRect.GetLength(0))
            {
                _y = _mapCellRect.GetLength(0) - 1;
            }
            else if (_y < 0) { _y = 0; }
            //同上
            int _x = (int)(_mapImagePos.X + p.X) / _w;
            if (_x >= _mapCellRect.GetLength(1))
            {
                _x = _mapCellRect.GetLength(1) - 1;
            }
            else if (_x < 0) { _x = 0; }

            //非地图操作区
            if (e.X < _mapShowRect.X || e.X > _mapShowRect.Right || e.Y < _mapShowRect.Y || e.Y > _mapShowRect.Bottom)
            {
                return;
            }
            _mouseDown = true;
            if (e.Button == MouseButtons.Left
                && (radioButton_brush.Checked || radioButton_eraser.Checked) )
            {
                _yLast = -1;
                _xLast = -1;
                _g = Graphics.FromImage(_mapImageBuffer);
            }
            else if (e.Button == MouseButtons.Right
                || (e.Button == MouseButtons.Left && radioButton_move.Checked)) 
            {
                _moveLastPos = new PointF((e.X - _mapShowRect.X) / _percent, (e.Y - _mapShowRect.Y) / _percent);
            }
            else if (radioButton_bucket.Checked)
            {
                _g = Graphics.FromImage(_mapImageBuffer);
                FillAround(_y, _x);
                ShowImageBuffer();
            }
        }

        private void Form1_MouseLeave(object sender, EventArgs e)
        {
            _mouseDown = false;
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            //刚开始可能有消息
            if (_mapCellRect == null || !_mouseDown)
            {
                return;
            }
            
            //坐标根据比例变换
            PointF p = new PointF((e.X - _mapShowRect.X) / _percent, (e.Y - _mapShowRect.Y) / _percent);

            //换算位置
            int _y = (int)(_mapImagePos.Y + p.Y) / _h;
            //控制位置不出界
            if (_y >= _mapCellRect.GetLength(0))
            {
                _y = _mapCellRect.GetLength(0) - 1;
            }
            else if (_y < 0){ _y = 0; }
            //同上
            int _x = (int)(_mapImagePos.X + p.X) / _w;
            if (_x >= _mapCellRect.GetLength(1))
            {
                _x = _mapCellRect.GetLength(1) - 1;
            }
            else if (_x < 0) { _x = 0; }

            bool _move;//是否是移动模式
            _move = false;

            if (e.Button == MouseButtons.Left)
            {
                if (radioButton_brush.Checked)
                {
                    //画笔模式
                    if (_mapCollision[_y, _x] != true)
                    {
                        SolidBrush sb = new SolidBrush(_collisionColor);
                        _g.FillRectangle(sb, _mapCellRect[_y, _x]);
                        _mapCollision[_y, _x] = true;
                    }
                    if (_xLast != -1 && _yLast != -1
                        && (Math.Abs( _y-_yLast)>1 ||Math.Abs( _x-_xLast)>1))
                    {
                        //_DrawLine(_x, _y, _xLast, _yLast);
                        Bresenham_Line(_x, _y, _xLast, _yLast);
                    }
                }
                else if (radioButton_eraser.Checked)
                {
                    //橡皮擦模式
                    if (_mapCollision[_y, _x])
                    {
                        _mapCollision[_y, _x] = false;
                        _g.FillRectangle(_mapTexBrush, _mapCellRect[_y, _x]);
                    }
                }
                else if (radioButton_move.Checked)
                {
                    _move = true;
                }
            }
            else if (e.Button == MouseButtons.Right )
            {
                _move = true;
            }
            if (_move == true)
            {
                //移动模式
                float xChange = ((p.X - _moveLastPos.X));
                float yChange = ((p.Y - _moveLastPos.Y));
                _mapImagePos.X -= xChange;
                _mapImagePos.Y -= yChange;
                _moveLastPos = p;
            }
            _yLast = _y;
            _xLast = _x;
            ShowImageBuffer();
        }
        private void FillAround(int y,int x)
        {
            if (_mapCollision[y, x] != true)
            {
                SolidBrush sb = new SolidBrush(_collisionColor);
                _g.FillRectangle(sb, _mapCellRect[y, x]);
                _mapCollision[y, x] = true;
            }
            if (y != 0
            && _mapCollision[y - 1, x] == false)
            {
                FillAround(y - 1, x);
            }
            if (y != _mapCellRect.GetLength(0)-1
                && _mapCollision[y + 1, x] == false)
            {
                FillAround(y + 1, x);
            }
            if (x != 0
            && _mapCollision[y , x-1] == false)
            {
                FillAround(y , x-1);
            }
            if (x != _mapCellRect.GetLength(1) -1
                && _mapCollision[y,x+1]==false)
            {
                FillAround(y , x+1);
            }
        }
        private void textBox_percent_TextChanged(object sender, EventArgs e)
        {
            ShowImageBuffer();
        }

        private void textBox_percent_KeyPress(object sender, KeyPressEventArgs e)
        {
            e.Handled = true; //不允许输入
        }

        private void button2_Click_1(object sender, EventArgs e)
        {
            _percent += 0.25f;
            textBox_percent.Text = (_percent * 100).ToString();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (_percent > 0.25f)
            {
                _percent -= 0.25f;
                textBox_percent.Text = (_percent * 100).ToString();
            }
        }

        private void button_default_Click(object sender, EventArgs e)
        {
            _percent = 1.0f;
            textBox_percent.Text = "100";
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (_mapImage == null)
            {
                MessageBox.Show("木有地图!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            saveFileDialog1.Filter = "地图碰撞文件(*.collision)|*.collision";
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                FileStream stream = File.Open(saveFileDialog1.FileName, FileMode.OpenOrCreate, FileAccess.Write);
                BinaryWriter writer = new BinaryWriter(stream);
                //  struct CollisionFile 保存的文件结构
                //  {
                //      单元格的大小
                //      int     cellWidth;
                //      int     cellHeight;
                //      单元格数目
                //      int     rowCount;
                //      int     columnCount;
                //      byte[,]  data;
                //  }   
                writer.Write(_w);
                writer.Write(_h);
                writer.Write(_mapCellRect.GetLength(0));
                writer.Write(_mapCellRect.GetLength(1));
                int row = 0, column = 0;
                
                //行数不变
                row = _mapCellRect.GetLength(0);
                
                if (_mapCellRect.GetLength(1) % 8 != 0)
                {
                    //列不被8整除 列的最后一个字节还剩几个位
                    column = _mapCellRect.GetLength(1) / 8 + 1;
                }
                else
                {
                    //刚好整除再好不过
                    column = _mapCellRect.GetLength(1) / 8;
                }
                Byte[,] data = new Byte[row,column];
                for(int i = 0; i < _mapCellRect.GetLength(0); ++i)
                {
                    for(int j = 0; j < _mapCellRect.GetLength(1); ++j)
                    {
                        row = i;
                        if (j % 8 != 0) { column = j / 8; }
                        else { column = j / 8; }

                        if (_mapCollision[i,j])
                        {
                            data[row,column] |= (Byte)(1<<(j % 8));
                        }
                        else
                        {
                            data[row,column] &= (Byte)(0xFF^(1<<(j % 8)));
                        }
                    }
                }
                writer.Close();
            }
        }


        void Bresenham_Line(int x1, int y1, int x2, int y2)
        {
             int dx,dy,p,const1,const2,x,y,inc;

             int steep = (Math.Abs(y2 - y1) > Math.Abs(x2 - x1)) ? 1 : 0;
             if(steep == 1)
             {
                 SwapInt(ref x1, ref y1);
                 SwapInt(ref x2, ref y2);
             }
             if(x1 > x2)
             {
                 SwapInt(ref x1, ref x2);
                 SwapInt(ref y1, ref y2);
             }
             dx = Math.Abs(x2 - x1);
             dy = Math.Abs(y2 - y1);   
             p = 2 * dy - dx;   
             const1 = 2 * dy;  
             const2 = 2 * (dy - dx); 
             x = x1;   
             y = y1; 
             inc = (y1 < y2) ? 1 : -1;
             while(x <= x2)
             {
                 if (steep == 1)
                 {
                     if (_mapCollision[x, y] != true)
                     {
                         SolidBrush sb = new SolidBrush(_collisionColor);
                         _g.FillRectangle(sb, _mapCellRect[x, y]);
                         _mapCollision[x, y] = true;
                     }
                     if (_mapCollision[x, y] != true)
                     {
                         SolidBrush sb = new SolidBrush(_collisionColor);
                         _g.FillRectangle(sb, _mapCellRect[x, y]);
                         _mapCollision[x, y] = true;
                     }
                 }
                 else
                 {
                     if (_mapCollision[y, x] != true)
                     {
                         SolidBrush sb = new SolidBrush(_collisionColor);
                         _g.FillRectangle(sb, _mapCellRect[y, x]);
                         _mapCollision[y, x] = true;
                     }
                     if (_mapCollision[y, x] != true)
                     {
                         SolidBrush sb = new SolidBrush(_collisionColor);
                         _g.FillRectangle(sb, _mapCellRect[y, x]);
                         _mapCollision[y, x] = true;
                     }
                 }
                 x++;   
                 if(p<0)   
                     p += const1;   
                 else
                 {   
                     p += const2;   
                     y += inc;   
                 }   
             }   
         }

        private void SwapInt(ref int a, ref int b) { int t; t = a; a = b; b = t; }
    }
}
